package cn.nubia.admin.common.shiro.handler;

import cn.nubia.admin.common.shiro.annotation.RequirePermissionExt;
import cn.nubia.admin.common.shiro.util.Classes;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.shiro.aop.MethodInvocation;
import org.apache.shiro.util.ThreadContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by Administrator on 2014/10/30.
 */
public class ExtCodeConverter {
    private final Logger logger = LoggerFactory.getLogger(ExtCodeConverter.class);

    private final Pattern paramPattern = Pattern.compile("\\{(\\w+)\\}");

    private final Pattern contextPattern = Pattern.compile("\\((\\w+)\\)");

    public List<String> valToCode(MethodInvocation methodInvocation , RequirePermissionExt ext) {
        String[] authCodes = ext.value();
        String[] fieldNames = ext.fieldNames();

        if (fieldNames == null) {
            fieldNames = new String[0];
        }

        if (authCodes.length != fieldNames.length) {
            fieldNames = fillUnsetVal(authCodes, fieldNames);
        }

        List<String> addParamCodes = new ArrayList<String>();
        try {
             for (int i=0;i<authCodes.length ;i ++) {
                String code = authCodes[i];
                String fieldName = fieldNames[i];

                String paramName = extractParamNames(code , paramPattern);
                if (paramName == null) {
                    addParamCodes.add(code);
                    continue;
                }

                Object value = getValue(methodInvocation, paramName);
                if (value instanceof Object[]) {
                    for (Object obj : (Object[]) value) {
                        addParamCodes.add(convertParamId(code, obj, fieldName));
                    }
                    continue;
                }
                if (value instanceof List) {
                    for (Object obj : (List) value) {
                        addParamCodes.add(convertParamId(code, obj, fieldName));
                    }
                    continue;
                }

                 if (value != null) {
                    addParamCodes.add(convertParamId(code, value, fieldName));
                    continue;
                }
                addParamCodes.add(code);
            }
            for (int  i= 0;i<addParamCodes.size();i++) {

                String code = convertContextId(addParamCodes.get(i) );
                addParamCodes.set(i , code);
            }

        }catch (Exception e) {
            logger.error("转换id失败" , e);
        }
        return addParamCodes;
    }

    /**
     * 根据原有长度填充数组,来解决注解中参数长度定义不一致问题
     * @param authCodes
     * @param fieldNames
     * @return
     */
    private String[] fillUnsetVal(String[] authCodes, String[] fieldNames) {
        String[] tmpFieldNames = new String[authCodes.length];
        for (int i = 0;i < authCodes.length; i++) {
            if (i < fieldNames.length) {
                tmpFieldNames[i] = fieldNames[i];
            }else {
                tmpFieldNames[i] = "";
            }
        }
        fieldNames = tmpFieldNames;
        return fieldNames;
    }




    public String convertParamId(String authCode, Object value, String fieldName) throws Exception{
        if (fieldName == null || StringUtils.isEmpty(fieldName)) {
            return convertId(authCode, value, paramPattern);
        }

        String id = BeanUtils.getProperty(value, fieldName);
        return convertId(authCode , id , paramPattern);
    }

    public String convertContextId(String authCode) {
        String contextParamName = extractParamNames(authCode , contextPattern);
        if (contextParamName == null) {
            return authCode;
        }
        Object value = ThreadContext.get(contextParamName);
        if (value == null) {
            return authCode;
        }

        return convertId(authCode , value , contextPattern);
    }

    public String convertId(String authCode , Object id , Pattern pattern) {
        Matcher matcher = pattern.matcher(authCode);
        StringBuffer sb = new StringBuffer();
        if (matcher.find()) {
            matcher.appendReplacement(sb ,id.toString());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    public String extractParamNames(String code , Pattern pattern) {
        Matcher matcher = pattern.matcher(code);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }


    public Object getValue(MethodInvocation methodInvocation , String paramName) {
        Object[] objects = methodInvocation.getArguments();

        String[] paramNames = Classes.getMethodParamNames(methodInvocation.getMethod());

        Object object = null;
        for (int i = 0;i<paramNames.length;i++) {
            if (paramName.equals(paramNames[i])) {
                object = objects[i];
            }
        }
        return object;
    }


    public static void main(String[] args) throws Exception {
        ExtCodeConverter converter = new ExtCodeConverter();
        String[] authCodes = new String[]{"a","b","c"};
        String[] fieldNames = new String[]{};
        System.out.println(Arrays.toString(converter.fillUnsetVal(authCodes , fieldNames)));
    }



}
